#pragma once

#include "DLTerm.hpp"
#include "../OperatorTerm.hpp"

namespace terms {
	namespace dl {
		template <typename Term, typename L=std::string, typename I=std::string, typename C=std::string, typename R=std::string>
		class DLOperatorTerm
			: public OperatorTerm<DLTerm<L, I, C, R>, Term>
		{	
		public:
			using OperatorTerm<DLTerm<L, I, C, R>, Term>::OperatorTerm;

		public:
			virtual const char *get_operator_name() const = 0;

		public:
			virtual std::string
			to_string() const
			{
				std::string str = "(";
				str.append(get_operator_name());
				str.append(" ");
				str.append(terms::TermList<DLTerm<L, I, C, R>, Term>::to_string(" "));
				str.append(")");
				return str;
			}
		};


		template <const char *op_name, terms::TermListType list_type, typename Term, typename L=std::string, typename I=std::string, typename C=std::string, typename R=std::string>
		class DLTypedOperatorTerm
			: public DLOperatorTerm<Term, L, I, C, R>
		{
		public:	
			template <typename Iter>
			DLTypedOperatorTerm(const Iter &b, const Iter &e)
				: DLOperatorTerm<Term, L, I, C, R>(list_type, b, e)
			{
			}

		public:	
			template <typename Container>
			DLTypedOperatorTerm(const Container &container)
				: DLOperatorTerm<Term, L, I, C, R>(list_type, container)
			{
			}

		public:
			virtual const char *
			get_operator_name() const
			{
				return op_name;
			}
		};


		template <const char *op_name, typename Term, typename L=std::string, typename I=std::string, typename C=std::string, typename R=std::string>
		class DLUnaryOperatorTerm
			: public DLTypedOperatorTerm<op_name, terms::TermListType::UNIQUE, Term, L, I, C, R>
		{
		public:
			DLUnaryOperatorTerm(const Term *term)
				: DLTypedOperatorTerm<op_name, terms::TermListType::UNIQUE, Term, L, I, C, R>(std::array<const Term *, 1>{{term}})
			{
			}

		public:
			const Term *
			get_term() const
			{
				return this->front();
			}
		};
	}
}
